/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package AlgoritmoPronostico;

import beans.Mapa;
import beans.Pedido;
import beans.Punto;
import beans.Vehiculo;
import colonia.MatrizFeromona;
import colonia.Nodo;
import AlgoritmoPronostico.GestorVehiculos;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;

/**
 *
 * @author Ahmed
 */
public class Algoritmo {
    public static ArrayList<ArrayList<Nodo>> mejoresHormigas;
    public static ArrayList<ArrayList<Pedido>> pedidosAsignados;
    public static MatrizFeromona feromonas;
    public static ArrayList<Nodo> ordenes = new ArrayList<>();
    public static ArrayList<Nodo> mapaNodos = new ArrayList<>();
    public static final float valorInicialFeromona = 1;
    public static final int numHormigas = 3;

    public static void initFeronoma() {
        feromonas = MatrizFeromona.getInstance(mapaNodos, valorInicialFeromona);
    }
    public static void desordenar(ArrayList<Nodo> lista){
        int size = lista.size();
        if (size < 3) return;
        int index;
        for(int i=0; i < size; i++){
            index = (int)Math.random()*(size-1);
            Nodo nodo = lista.get(index);
            lista.set(index,lista.get(i));
            lista.set(i,nodo);
        }
    }

    public static ArrayList<Hormiga> crearHormigas(int numHormigas, Vehiculo v) {
        ArrayList<Hormiga> list = new ArrayList<>();
        for (int i = 0; i < numHormigas; i++) {
            Hormiga newHormiga = new Hormiga(v);
            list.add((newHormiga));
        }
        return list;
    }

    public static void crearNodosMapa() {
        int nNodos = Mapa.getPuntos().size();
        for (int i = 0; i < nNodos; i++) {
            Punto p = Mapa.getPuntos().get(i);
            Nodo node = new Nodo(p);
            mapaNodos.add(node);
        }
    }

    public static void crearListaOrdenes(ArrayList<Pedido> pedidos) {
        for (Pedido pedido:pedidos) {
            Punto p = pedido.getDestino();
            Nodo n = mapaNodos.get(Mapa.getIndicePunto(p.getX(), p.getY()));
            n.setDemand(pedido.getDemanda());
            n.setExpirationDate(pedido.getFechaVencimiento());
            n.setIdPedido(pedido.getIdPedido());
            if (!ordenes.contains(n))
                ordenes.add(n);
        }
    }

    public static void actualizar(Hormiga hormiga) {
        int cap = hormiga.getCapacidadInicial();
        int rSize = hormiga.getRoute().size();
        for (int i = 0; i < rSize; i++) {
            int dem = hormiga.getRoute().get(i).getDemand();
            if (cap >= dem) {
                hormiga.getRoute().get(i).setDemand(0);
                ordenes.remove(hormiga.getRoute().get(i));
                cap -= dem;
            } else {
                hormiga.getRoute().get(i).setDemand(dem - cap);
                cap = 0;
            }
        }
    }

    public static void obtenerSolucion(ArrayList<Pedido> pedidos) {
        ordenes = new ArrayList<>();
        crearListaOrdenes(pedidos);
        ArrayList<Pedido> pedidosTemp = (ArrayList<Pedido> ) pedidos.clone();
        ArrayList<Vehiculo> vehiculos = GestorVehiculos.getInstancia().getVehiculos();
        mejoresHormigas = new ArrayList<ArrayList<Nodo>>();
        pedidosAsignados = new ArrayList<ArrayList<Pedido>>();
        //initializeArcsPheromoneLevel();//se puede resetear o tener algun valor residual
        int vSize = vehiculos.size();
        for (int n = 0; n < vSize; n++) {
            //variables para guardar la mejor solucion
            if (!vehiculos.get(n).disponible) {
                mejoresHormigas.add(new ArrayList<Nodo>());
                pedidosAsignados.add(new ArrayList<Pedido>());
                continue;
            }
            //float bestCost = Float.POSITIVE_INFINITY;
            float bestTime = Float.POSITIVE_INFINITY;
            Hormiga mejorHormiga = null; 
            //se crean las hormigas
            ArrayList<Hormiga> listaHormigas = crearHormigas(numHormigas, vehiculos.get(n));
            for (int i = 0; i < listaHormigas.size(); i++) {
                //crear una lista temporal de las pedidos por cada hormiga
                ArrayList<Nodo> tempOrders = (ArrayList<Nodo>) ordenes.clone();
                ArrayList<Pedido> tempPedidos = (ArrayList<Pedido>) pedidosTemp.clone();
                Hormiga hormigaActual = listaHormigas.get(i); 
                while (!hormigaActual.isDone() && !tempOrders.isEmpty()) {
                    desordenar(tempOrders);
                    hormigaActual.selectNextDestination(tempOrders, tempPedidos, feromonas);
                    feromonas.updatePheromoneTrail(hormigaActual.getRoute());
                }
                //volver al almacen una vez que se acaba
                Punto almacenP = Mapa.getAlmacen();
                Nodo almacen = mapaNodos.get(Mapa.getIndicePunto(almacenP.getX(), almacenP.getY()));
                hormigaActual.getRoute().add(almacen); 
                //de todas las hormigas cual tuvo el mejor costo
                float currentTime = hormigaActual.calculateTimeRuta();
                //float currentCost = hormigaActual.calculateCostoRuta();
                if (currentTime <= bestTime) {
                    bestTime = currentTime;
                    mejorHormiga = hormigaActual;
                }  
            }
            
            actualizar(mejorHormiga);
            feromonas.actualizarMejorRastroFeromona(mejorHormiga.getRoute(), bestTime);
            mejoresHormigas.add(mejorHormiga.getRoute());
            pedidosAsignados.add(mejorHormiga.getPedidos());
        }
    }
    
    public static boolean siguienteOrden(ArrayList<Hormiga> hormigas, ArrayList<Nodo> orders,ArrayList<Pedido> pedidos){
        int m = 0;
        while (true){
            if (m >= hormigas.size())
                break;
            Hormiga hormiga = hormigas.get(m);
            if (hormiga.getCapacidadActual() == 0 || hormiga.isDone()){
               hormigas.remove(hormiga);
               m = -1;
            } 
            m++;
        }
        if(orders.isEmpty() || hormigas.isEmpty())return false;
        float Q = Hormiga.Q;
        Nodo ordenSelected = orders.get(0);
        float maxAtractiveness =0;
        int maxIndex=0;
        float sum =0;
        Hormiga selected=null;
        ArrayList<Float> probAcum = new ArrayList<>();
        boolean posible = false;
        for(int i=0,size = hormigas.size(); i<size ;i++){
            if (hormigas.get(i).posible(ordenSelected)){
                posible = true;
                break;
            }
        }
        if (posible)
            removerImp(hormigas,ordenSelected);
        for(int i=0,size = hormigas.size(); i<size ;i++){
            float t = hormigas.get(i).calcularImportanciaCamino(ordenSelected, feromonas);
            float n;
            if (posible){
                n = hormigas.get(i).calculateTrailAtractivenessInversePos(ordenSelected);
            }else{
                n = hormigas.get(i).calculateTrailAtractivenessInverseImpos(ordenSelected);
            }
            float product = (float) (t * Math.pow(n, Hormiga.BETA));
            sum += product;
            probAcum.add(sum);
            if(product > maxAtractiveness){
                maxAtractiveness = product;
                maxIndex = i;
            }
        }
        if(maxAtractiveness==0){
            return false;
        }
        if(Math.random() <= Q){
             selected= hormigas.get(maxIndex);
        }
        else{
            float p = (float)(Math.random()*probAcum.get(probAcum.size()-1));
            for (int i = 0; i < probAcum.size(); i++) {
                if (p <= probAcum.get(i)) {
                    selected = hormigas.get(i);
                    break;
                }
            }
        }
        if(selected == null){
            selected = hormigas.get(maxIndex);
        }
        //actualizar capacidadlected, this._route.get(this._route.size()-1)) * this._fuelConsumedPerKm;
        int etwa = (Nodo.geomDistance(selected.getRoute().get(selected.getRoute().size()-1), ordenSelected)*3600/selected.getSpeed()) ;
        selected.getLastTemporalLocation().add(Calendar.SECOND, etwa);
        selected.getRoute().add(ordenSelected);
        if (orders.contains(ordenSelected)){
            Pedido pedidoSelected = Hormiga.buscaPedidos(pedidos,ordenSelected);
            if (pedidoSelected != null){
                selected.getPedidos().add(pedidoSelected);
                pedidos.remove(pedidoSelected);
            }
        }
        
        if(selected.getCapacidadActual()< ordenSelected.getDemand()){
            ordenSelected.setDemand(ordenSelected.getDemand() - selected.getCapacidadActual());
            selected.setCapacidadActual(0);
            selected.termino();
        }
        else{
            selected.setCapacidadActual(selected.getCapacidadActual()-ordenSelected.getDemand());
            orders.remove(ordenSelected);
        }
        return true;
    }
    
    private static void removerImp(ArrayList<Hormiga> hormigas, Nodo ordenSelected) {
        int k = 0;
        while (true){
            if (k >= hormigas.size()) break;
            Hormiga hormiga = hormigas.get(k);
            if (!hormiga.posible(ordenSelected)){
                hormigas.remove(hormiga);
                k = -1;
            }
            k++;
        }
    }

    public static void obtenerSolucionInversa(ArrayList<Pedido> pedidos) {
        ordenes = new ArrayList<>();
        crearListaOrdenes(pedidos);
        ArrayList<Pedido> pedidosTemp = (ArrayList<Pedido> ) pedidos.clone();
        ArrayList<Vehiculo> vehiculos = GestorVehiculos.getInstancia().getVehiculos();
        mejoresHormigas = new ArrayList<ArrayList<Nodo>>();
        pedidosAsignados = new ArrayList<ArrayList<Pedido>>();
        Punto almacenP = Mapa.getAlmacen();
        Nodo almacen = mapaNodos.get(Mapa.getIndicePunto(almacenP.getX(), almacenP.getY()));
        int nVehiculos = vehiculos.size();
        ArrayList<ArrayList<Hormiga>> listaHormigas = new ArrayList<>();
        for(int i=0; i< numHormigas;i++) 
            listaHormigas.add(new ArrayList<Hormiga>());
        ArrayList<Hormiga> hormigas;
        for(int i=0; i< nVehiculos;i++){
            hormigas = crearHormigas(numHormigas,vehiculos.get(i));
            for(int j=0;j< numHormigas;j++){
                listaHormigas.get(j).add(hormigas.get(j));
                hormigas.get(j).setLastTemporalLocation((Calendar)Reloj.getInstancia().getFecha().clone());
            }
        }
        Collections.sort(ordenes, new Comparator(){
            @Override
            public int compare(Object o1, Object o2) {
                return ((Nodo)o1).getExpirationDate().compareTo(((Nodo)o2).getExpirationDate());
            }
            
        });
        ArrayList<Nodo> tempOrders = (ArrayList<Nodo>) ordenes.clone();
        ArrayList<Pedido> tempPedidos = (ArrayList<Pedido>) pedidosTemp.clone();
        float bestCost = Float.POSITIVE_INFINITY;
        int bestSet=0;
        ArrayList<Hormiga> hormigasAux;
        for(int i=0; i < numHormigas;i++){
            tempPedidos = (ArrayList<Pedido>) pedidosTemp.clone();
            tempOrders = (ArrayList<Nodo>) ordenes.clone();
            hormigasAux = (ArrayList<Hormiga>)listaHormigas.get(i).clone();
            while(siguienteOrden(hormigasAux,tempOrders,tempPedidos)){
                hormigasAux = (ArrayList<Hormiga>)listaHormigas.get(i).clone();
            }
            float costoSol = 0;
            for(int j=0; j < nVehiculos;j++){
                listaHormigas.get(i).get(j).getRoute().add(almacen);
                feromonas.updatePheromoneTrail(listaHormigas.get(i).get(j).getRoute());
                costoSol+=listaHormigas.get(i).get(j).calculateCostoRuta();
            }
            if(costoSol < bestCost){
                bestCost = costoSol;
                bestSet= i;
            }
        }
        ArrayList<Nodo> bestRoute;
        for(int j=0; j< nVehiculos;j++){
            bestRoute =listaHormigas.get(bestSet).get(j).getRoute();
            feromonas.actualizarMejorRastroFeromona(bestRoute, bestCost);
            mejoresHormigas.add(bestRoute);
            pedidosAsignados.add(listaHormigas.get(bestSet).get(j).getPedidos());
        }
    }
    
//    public static void obtenerSolucionInversa2(ArrayList<Pedido> pedidos) {
//        ordenes = new ArrayList<>();
//        crearListaOrdenes(pedidos);
//        ArrayList<Pedido> pedidosTemp = (ArrayList<Pedido> ) pedidos.clone();
//        ArrayList<Vehiculo> vehiculos = AlgoritmoPronostico.GestorVehiculos.getInstancia().getVehiculos();
//        mejoresHormigas = new ArrayList<ArrayList<Nodo>>();
//        pedidosAsignados = new ArrayList<ArrayList<Pedido>>();
//        Punto almacenP = Mapa.getAlmacen();
//        Nodo almacen = mapaNodos.get(Mapa.getIndicePunto(almacenP.getX(), almacenP.getY()));
//        int nVehiculos = vehiculos.size();
//        ArrayList<ArrayList<Hormiga>> listaHormigas = new ArrayList<>();
//        for(int i=0; i< numHormigas;i++) 
//            listaHormigas.add(new ArrayList<Hormiga>());
//        ArrayList<Hormiga> hormigas;
//        for(int i=0; i< nVehiculos;i++){
//            hormigas = crearHormigas(numHormigas,vehiculos.get(i));
//            for(int j=0;j< numHormigas;j++){
//                listaHormigas.get(j).add(hormigas.get(j));
//                hormigas.get(j).setLastTemporalLocation((Calendar)Reloj.getInstancia().getFecha().clone());
//            }
//        }
//        Collections.sort(ordenes, new Comparator(){
//            @Override
//            public int compare(Object o1, Object o2) {
//                return ((Nodo)o1).getExpirationDate().compareTo(((Nodo)o2).getExpirationDate());
//            }
//            
//        });
//        ArrayList<Nodo> tempOrders = (ArrayList<Nodo>) ordenes.clone();
//        ArrayList<Pedido> tempPedidos = (ArrayList<Pedido>) pedidosTemp.clone();
//        float bestTime = Float.POSITIVE_INFINITY;
//        int bestSet=0;
//        ArrayList<Hormiga> hormigasAux;
//        for(int i=0; i < numHormigas;i++){
//            tempPedidos = (ArrayList<Pedido>) pedidosTemp.clone();
//            tempOrders = (ArrayList<Nodo>) ordenes.clone();
//            hormigasAux = (ArrayList<Hormiga>)listaHormigas.get(i).clone();
//            while(siguienteOrden(hormigasAux,tempOrders,tempPedidos)){
//                hormigasAux = (ArrayList<Hormiga>)listaHormigas.get(i).clone();
//            }
//            float costoSol = 0;
//            for(int j=0; j < nVehiculos;j++){
//                listaHormigas.get(i).get(j).getRoute().add(almacen);
//                feromonas.updatePheromoneTrail(listaHormigas.get(i).get(j).getRoute());
//                costoSol+=listaHormigas.get(i).get(j).calculateTimeRuta();
//            }
//            if(costoSol < bestTime){
//                bestTime = costoSol;
//                bestSet= i;
//            }
//        }
//        ArrayList<Nodo> bestRoute;
//        for(int j=0; j< nVehiculos;j++){
//            bestRoute =listaHormigas.get(bestSet).get(j).getRoute();
//            feromonas.actualizarMejorRastroFeromona(bestRoute, bestTime);
//            mejoresHormigas.add(bestRoute);
//            pedidosAsignados.add(listaHormigas.get(bestSet).get(j).getPedidos());
//        }
//    }
}
